LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;

entity coco is
  port ( 
    CLK			    :		in	std_logic;  -- clock signal
    nReset		  :		in	std_logic;		-- reset for processor
    
    adMemRead0      : in std_logic;                       -- dcache side
    adMemWrite0     : in std_logic;                       -- dcache side
    adMemWait0      : out  std_logic;                       -- dcache side
    adMemAddr0      : in std_logic_vector (15 downto 0);  -- dcache side
    adMemDataRead0  : out  std_logic_vector (31 downto 0);  -- dcache side
    adMemDataWrite0 : in std_logic_vector (31 downto 0);   -- dcache side
    
    adMemRead1      : in std_logic;                       -- dcache side
    adMemWrite1     : in std_logic;                       -- dcache side
    adMemWait1      : out  std_logic;                       -- dcache side
    adMemAddr1      : in std_logic_vector (15 downto 0);  -- dcache side
    adMemDataRead1  : out  std_logic_vector (31 downto 0);  -- dcache side
    adMemDataWrite1 : in std_logic_vector (31 downto 0);   -- dcache side
    
    
    cocoMemRead      : out std_logic;                       -- arbitrator side
    cocoMemWrite     : out std_logic;                       -- arbitrator side
    cocoMemWait      : in  std_logic;                       -- arbitrator side
    cocoMemAddr      : out std_logic_vector (15 downto 0);  -- arbitrator side
    cocoMemDataRead  : in  std_logic_vector (31 downto 0);  -- arbitrator side
    cocoMemDataWrite : out std_logic_vector (31 downto 0);   -- arbitrator side
    
    prMemRead0       :  in std_logic;
    prMemWrite0      :  in std_logic;
    prMemAddr0       :  in std_logic_vector(15 downto 0);

    prMemRead1       :  in std_logic;
    prMemWrite1      :  in std_logic;
    prMemAddr1       :  in std_logic_vector(15 downto 0);
    
    snoop_read0      :  out std_logic;
    snoop_write0     :  out std_logic;
    snoop_valid0     :  in  std_logic;
    extwrite0        :  in  std_logic;
    snoop_ReadData0  :  in  std_logic_vector(31 downto 0);
    
    snoop_read1      :  out std_logic;
    snoop_write1     :  out std_logic;
    snoop_valid1     :  in  std_logic;
    extwrite1        :  in  std_logic;
    snoop_ReadData1  :  in  std_logic_vector(31 downto 0)

  );
  
end coco;

architecture struct of coco is
  
  signal chooseD0, chooseD1 : std_logic;
  
  signal SR1, SR0, SW0, SW1  : std_logic;
  
  signal SnoopWB0, SnoopWB1 : std_logic;

  type PRINTFS is (NORMAL, ERROR, HERE0, HERE1, HERE2, HERE3, HERE4, HERE5, HERE6, HERE7);
  signal status : PRINTFS;
  
  signal priority, next_priority : std_logic;


  type states is (IDLE, DC0_BusRead, MISS0, MISS1, DC1_BusRead, DC0_I_PrW, DC1_I_PrW, DC0_S_PrW, DC1_S_PrW, SNOOP);
  signal state, nextstate : states;
begin

  snoop_read0 <= SR0;
  snoop_read1 <= SR1;
  snoop_write0 <= SW0;
  snoop_write1 <= SW1;
  
statereg : process(CLK, nReset)
   begin
     if nReset = '0' then
       priority <= '0';
       state <= IDLE;
     elsif rising_edge(CLK) then
       state <= nextstate;
       priority <= next_priority;
     end if;
   end process statereg;


adMemDataRead0 <= snoop_readdata1 when SR1 = '1' and snoop_valid1 = '1' else cocoMemDataRead;
adMemDataRead1 <= snoop_readdata0 when SR0 = '1' and snoop_valid0 = '1' else cocoMemDataRead;

next_state : process(state, prMemRead1, prMemRead0, adMemRead1, adMemRead0,
                     prMemWrite0, prMemWrite1, extwrite0, extwrite1, snoop_valid1, snoop_valid0,
                     priority, admemwrite0, admemwrite1)
begin
      nextstate <= state;
      status <= NORMAL;

      next_priority <= priority;
      SR0 <= '0';
      SR1 <= '0';
      SW0 <= '0';
      SW1 <= '0';
      chooseD0 <= '0';
      chooseD1 <= '0';
      SnoopWB0 <= '0';
      SnoopWB1 <= '0';
      
      
  case state is
       
      when IDLE =>
        
        if ((priority = '0' and (prMemRead0 = '1' or prMemWrite0 = '1')) or (prMemRead1 = '0' and prMemWrite1 = '0')) then --serve Cache zero on its turn or if p1 is idle
          status <= HERE2;
          if (prMemRead0 = '1' or prMemWrite0 = '1') then
            chooseD0 <= '1';
            nextstate <= IDLE;
          end if;
          if(adMemRead0 = '1' or adMemWrite0 = '1') then
            if ((prMemRead0 = '1' and adMemWrite0 = '1') or (prMemWrite0 = '1' and adMemWrite0 = '1')) then
              chooseD0 <= '1';
              nextstate <= MISS0;
            elsif (prMemWrite0 = '1' and adMemRead0 = '1') then --PrWr / BusRdX (I -> M)
              status <= HERE1;
              SW1 <= '1'; --then forces writeback/invalidate
              SR1 <= '1'; --first grabs valid data
              nextstate <= DC0_I_PrW;
              chooseD0 <= '1';
            elsif (prMemRead0 = '1' and adMemRead0 = '1') then --PrRd / BusRd (I -> S)
              SR1 <= '1'; --check other cache for valid data
              nextstate <= DC0_BusRead;
              chooseD0 <= '1';
            else 
              STATUS <= ERROR;
            end if;
          elsif (prMemWrite0 = '1' and extwrite0 = '1') then -- PrWr / BusRdX (S -> M)
            status <= HERE4;
            SW1 <= '1'; --BusReadX
            chooseD0 <= '1';
            --next_priority <= '1';
            nextstate <= DC0_S_PrW;
          end if;
        else
          status <= HERE3;
          if (prMemRead1 = '1' or prMemWrite1 = '1') then
            chooseD1 <= '1';
            nextstate <= IDLE;
          end if;
          if (adMemRead1 = '1' or adMemWrite1 = '1') then
            if ((prMemRead1 = '1' and adMemWrite1 = '1') or (prMemWrite1 = '1' and adMemWrite1 = '1')) then
              chooseD1 <= '1';
              nextstate <= MISS1;
            elsif (prMemWrite1 = '1' and adMemRead1 = '1') then --PrWr / BusRdX (I -> M)
              SW0 <= '1'; --then forces writeback/invalidate
              SR0 <= '1'; --first grabs valid data
              nextstate <= DC1_I_PrW;
              chooseD1 <= '1';
            elsif (prMemRead1 = '1' and adMemRead1 = '1') then --PrRd / BusRd (I -> S)
              SR0 <= '1'; --check other cache for valid data
              nextstate <= DC1_BusRead;
              chooseD1 <= '1';
            else 
              STATUS <= ERROR;
            end if;
          elsif (prMemWrite1 = '1' and extwrite1 = '1') then -- PrWr / BusRdX (S -> M)
            SW0 <= '1'; --BusReadX
            chooseD1 <= '1';
            --next_priority <= '0';
            nextstate <= DC1_S_PrW;
          end if;
        end if;
        
      when MISS0 =>
        next_priority <= '0'; --because the allocate after a write back should be serviced immediately
        chooseD0 <= '1';
        if (adMemWrite0 = '0') then
          if (prMemWrite0 = '1' and adMemRead0 = '1') then --PrWr / BusRdX (I -> M)
              SW1 <= '1'; --then forces writeback/invalidate
              SR1 <= '1'; --first grabs valid data
              nextstate <= DC0_I_PrW;
          elsif (prMemRead0 = '1' and adMemRead0 = '1') then --PrRd / BusRd (I -> S)
            SR1 <= '1'; --check other cache for valid data
            nextstate <= DC0_BusRead;
          else
            nextstate <= IDLE;
          end if;
        end if;
        
      when MISS1 =>
        next_priority <= '1';
        chooseD1 <= '1';
        if (adMemWrite1 = '0') then
          if (prMemWrite1 = '1' and adMemRead1 = '1') then --PrWr / BusRdX (I -> M)
            SW0 <= '1'; --then forces writeback/invalidate
            SR0 <= '1'; --first grabs valid data
            nextstate <= DC1_I_PrW;
          elsif (prMemRead1 = '1' and adMemRead1 = '1') then --PrRd / BusRd (I -> S)
            SR0 <= '1'; --check other cache for valid data
            nextstate <= DC1_BusRead;
          else
            nextstate <= IDLE;
          end if;
        end if;
        
      when DC0_S_PrW =>
        chooseD0 <= '1';
        
        if (prMemWrite0 = '1' and extwrite0 = '1' and snoop_valid1 = '1') then
          SW1 <= '1';
        else
          nextstate <= IDLE;
          next_priority <= '1';
        end if;
-----------------------------------------------------------------        
      when DC1_S_PrW =>
        chooseD1 <= '1';
        
        if (prMemWrite1 = '1' and extwrite1 = '1' and snoop_valid0 = '1') then
          SW0 <= '1';
        else
          nextstate <= IDLE;
          next_priority <= '0';
        end if;
        
      when DC0_I_PrW => 
        chooseD0 <= '1';
        
        if (prMemWrite0 = '1' and adMemRead0 = '1') then --PrWr / BusRdX (I -> M)
          SW1 <= '1'; --then forces writeback/invalidate
          SR1 <= '1'; --first grabs valid data
          if (adMemWrite1 = '1') then
            SnoopWB1 <= '1';
          end if;
        else
          nextstate <= IDLE;
          next_priority <= '1';
        end if;
-------------------------------------------        
      when DC1_I_PrW => 
        chooseD1 <= '1';
        
        if (prMemWrite1 = '1' and adMemRead1 = '1') then --PrWr / BusRdX (I -> M)
          SW0 <= '1'; --then forces writeback/invalidate
          SR0 <= '1'; --first grabs valid data
          if (adMemWrite0 = '1') then
            SnoopWB0 <= '1';
          end if;
        else
          nextstate <= IDLE;
          next_priority <= '0';
        end if;        
        
        
      when DC0_BusRead =>
        chooseD0 <= '1';
        if (prMemRead0 = '1' and adMemRead0  = '1') then
          SR1 <= '1';
          if (adMemWrite1 = '1') then
            SnoopWB1 <= '1';
          end if;
        else
          nextstate <= IDLE;
          next_priority <= '1';
        end if;
----------------------------------------------------        
      when DC1_BusRead =>
        chooseD1 <= '1';
        if (prMemRead1 = '1' and adMemRead1  = '1') then
          SR0 <= '1';
          if (adMemWrite0 = '1') then
            SnoopWB0 <= '1';
          end if;
        else
          nextstate <= IDLE;
          next_priority <= '0';
        end if;
        
 
      when OTHERS =>
         nextstate <= IDLE;
         
       end case;
     end process;
     

 
  cocoMemWrite <= '1' when SnoopWB0 = '1' or SnoopWB1 = '1' else
                  '1' when ((adMemWrite0 = '1' and chooseD0 = '1') or (adMemWrite1 = '1' and chooseD1 = '1')) else
                  '0';
 
  cocoMemRead <= '0' when SR0 = '1' and snoop_valid0 = '1' else
                 '0' when SR1 = '1' and snoop_valid1 = '1' else
                 '1' when chooseD0 = '1' and adMemRead0 = '1' else
                 '1' when chooseD1 = '1' and adMemRead1 = '1'else
                 '0'; 
  
  
  cocoMemAddr <= admemAddr0 when SnoopWB0 = '1' else
                 adMemAddr1 when SnoopWB1 = '1' else
                 adMemAddr0 when chooseD0 = '1' else
                 adMemAddr1 when chooseD1 = '1' else
                  x"0AAA";
            
  cocoMemDataWrite <= adMemDataWrite0 when SnoopWB0 = '1' else
                      adMemDataWrite1 when SnoopWB1 = '1' else
                      adMemDataWrite0 when chooseD0 = '1' else
                      adMemDataWrite1 when chooseD1 = '1' else
                      x"BBBBDDDD";
  
  adMemWait0 <= '1' when SnoopWB1 = '1' else 
                cocoMemWait when SnoopWB0 = '1' else 
                (cocoMemWait and (adMemWrite0 or adMemRead0)) or ChooseD1;
  
  adMemWait1 <= '1' when SnoopWB0 = '1' else
                (cocoMemWait) when SnoopWB1 = '1' else 
                (cocoMemWait and (adMemWrite1 or adMemRead1)) or ChooseD0;
  

end struct;
